<?php if (!defined('CORE_DIR')) die('Wrong entry point!');

/* 
 * Copyright 2010 Anton Muraviev a.k.a kuromaisu
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */

Aero::define('AeroConstraints', AS_CLASS);
Aero::import('libs/pipeline');

class AeroConstraints extends AeroPipeline
{
	public function validate($value)
	//
	// Validates value via callback method.
	// 
	// Callback method is called for each value and segment.
	// 
	//     $callback($value,$segment,$arguments)
	// 
	// __Callback must return NULL for unrecognized segments.__
	// 
	// Parameters:
	// * `misc` -- a value or `array` of values.
	// * `string` -- _optional_ name of the callback function / method.
	// 
	// Returns:
	// * `true` -- if validation succeeded.
	// * `array(value,segment,attributes)` -- if validation failed.
	// 
	// If multiple values have been passed and at least one them was invalid
	// this method will return an array of results, e.g. 
	// 
	//     array(true,true,array('12','max_value',array(10)));
	# 
	{
		if (count($this->contexts) == 0)
		{
			$this->addContext($this);
		}
		
		if (is_array($value))
		{
			$results = array_map(array($this,'validate'), $value);
			$reduced = array_unique($results);
			
			if (count($reduced) == 1 && $reduced[0] === true)
			{
				return true;
			}
			
			return $results;
		}
		
		foreach ($this->segments as $attributes)
		{
			$result = null;
			$segment = array_shift($attributes);
			
			foreach ($this->contexts as $context)
			{
				if (is_object($context) && method_exists($context, 'constrain'))
				{
					$result = $context->constrain($value, $segment, $attributes);
				
					if ($result === true)
					{
						continue 2;
					}
					else if ($result === false)
					{
						return array($value, $segment, $attributes);
					}
				}
			}
			
			if (is_null($result))
			{
				trigger_error('Pipe segment <code>'.$segment.'[]</code> could not processed.', E_USER_NOTICE);
			}
		}
		
		return true;
	}
	
	public function constrain($value, $contraint, $args = array())
	// 
	// Callback method used by `validate()`.
	// 
	// Supported constraints:
	// 
	// * `required` -- value must non-empty string or valid numeric value.
	// * `numeric` -- any valid decimal number.
	// * `natural` -- any valid natural number.
	// * `greater_than_or_equal`, `gte` or `min_value` -- value must be greater than or equal to the first attribute.
	// * `greater_than` or `gt` -- value must be greater than the first attribute.
	// * `less_than_or_equal`, `lte` or `max_value` -- value must be less than or equal to the first attribute.
	// * `less_than` or `lt` -- value must be less than the first attribute.
	// * `between` -- value must be equal or greater than the first attribute and less than or equal to the second.
	// * `min_length` -- value length must be greater than or equal to the first attribute.
	// * `max_length` -- value length must be less than or equal to the first attribute.
	// * `longer_than` -- value length must be greater than the first attribute.
	// * `shorter_than` -- value length must be less than the first attribute.
	// 
	{
		switch ($contraint)
		{
			case 'required': 
				return !empty($value) || is_numeric($value);
			case 'numeric': 
				return 1 === preg_match('~^[+\-]?[0-9]+[\.,]?[0-9]*$~', $value);
			case 'natural':
				return 1 === preg_match('~^[0-9]+$~', $value);
			case 'gte':
			case 'min_value':
			case 'greater_than_or_equal':
				return isset($args[0]) && is_numeric($value) && $value >= $args[0];
			case 'gt':
			case 'greater_than':
				return isset($args[0]) && is_numeric($value) && $value > $args[0];
			case 'lte':
			case 'max_value':
			case 'less_than_or_equal':
				return isset($args[0]) && is_numeric($value) && $value <= $args[0];
			case 'lt':
			case 'less_than':
				return isset($args[0]) && is_numeric($value) && $value < $args[0];
			case 'between':
				return isset($args[0], $args[1]) 
					&& is_numeric($value) 
					&& $value <= max($args[0],$args[1])
					&& $value >= min($args[0],$args[1]);
			case 'min_length':
				return isset($args[0]) && is_numeric($args[0]) && strlen($value) >= $args[0];
			case 'longer_than':
				return isset($args[0]) && is_numeric($args[0]) && strlen($value) > $args[0];
			case 'max_length':
				return isset($args[0]) && is_numeric($args[0]) && strlen($value) <= $args[0];
			case 'shorter_than':
				return isset($args[0]) && is_numeric($args[0]) && strlen($value) < $args[0];
		}
		
		return null;
	}
}

